package com.dd.cloud.user.service.news.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.cloud.common.constants.NewsConstants;
import com.dd.cloud.common.constants.RedisKeyConstants;
import com.dd.cloud.common.constants.UserConstants;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.interceptor.UserInterceptor;
import com.dd.cloud.common.utils.DateUtils;
import com.dd.cloud.common.utils.JsonUtils;
import com.dd.cloud.common.utils.ValidatorUtils;
import com.dd.cloud.common.vo.LoginInfo;
import com.dd.cloud.user.entity.news.AppPromotionNews;
import com.dd.cloud.user.mapper.news.AppPromotionNewsMapper;
import com.dd.cloud.user.mq.MQConfig;
import com.dd.cloud.user.mq.RabbitSend;
import com.dd.cloud.user.req.news.AppPromotionNewsCreateReq;
import com.dd.cloud.user.req.news.AppPromotionNewsPageReq;
import com.dd.cloud.user.req.news.AppPromotionNewsUpdateReq;
import com.dd.cloud.user.res.news.AppPromotionNewsDetailRes;
import com.dd.cloud.user.res.news.AppPromotionNewsPageRes;
import com.dd.cloud.user.service.log.ILogErrorService;
import com.dd.cloud.user.service.news.IAppPromotionNewsService;
import com.dd.cloud.user.service.plat.ITrustDeviceService;
import com.dd.cloud.user.util.JiGuangPushUtil;
import com.dd.cloud.user.vo.DelayMqVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Lx
 * @version 1.0
 * @date 2023/9/14
 */
@Slf4j
@Service
public class AppPromotionNewsServiceImpl extends ServiceImpl<AppPromotionNewsMapper, AppPromotionNews> implements IAppPromotionNewsService {
    @Autowired
    private RabbitSend rabbitSend;
    @Autowired
    private ITrustDeviceService trustDeviceService;
    @Autowired
    private ILogErrorService logErrorService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 创建app消息推广记录
     *
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createAppPromotionNews(AppPromotionNewsCreateReq req) {
        if (req.getStatus() != 4) {
            String errStr = ValidatorUtils.verifyStr(req, req.getClass());
            if (errStr != null) {
                throw new ZekeException(400, errStr);
            }
        }
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.ADMIN).get();
        AppPromotionNews appPromotionNews = new AppPromotionNews();
        if (req.getStatus() == null || req.getStatus() != 4) {
            if (req.getSendDate() == null) {
                req.setSendDate(LocalDateTime.now());
                req.setStatus(2);
            } else {
                req.setStatus(1);
                appPromotionNews.setRemark("（于" + DateUtils.DTF_MD_HM.format(req.getSendDate()) + "时推送）");
            }
        }
        BeanUtils.copyProperties(req, appPromotionNews);
        appPromotionNews.setOperId(loginInfo.getId());
        appPromotionNews.setOperType(loginInfo.getType());
        appPromotionNews.setOperName(loginInfo.getName());
        appPromotionNews.setCreateDate(LocalDateTime.now());
        boolean flag = save(appPromotionNews);
        if (!flag) {
            throw new ZekeException(400, "app推送消息记录添加失败");
        }
        if (req.getStatus() == 4) {
            return;
        }
        //直接发送延时3秒发
        long millis = 3000;
        if (req.getStatus() == NewsConstants.PromotionNewsStatus.WEAT_SEND) {
            //延时毫秒值
            millis = Duration.between(appPromotionNews.getCreateDate(), appPromotionNews.getSendDate()).toMillis();
        }
        //定时推送消息
        DelayMqVo<String> delayMqVo = new DelayMqVo<>();
        delayMqVo.setT(String.valueOf(appPromotionNews.getId()));
        delayMqVo.setType(DelayMqVo.DelayMqVoType.APP_UNIPUSH_MSG);
        rabbitSend.sendMessageDelay(MQConfig.DELAY_EXCHANGE, MQConfig.DELAY_KEY, delayMqVo, millis);
    }

    /**
     * 修改app消息推广记录
     *
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAppPromotionNews(AppPromotionNewsUpdateReq req) {
        if (req.getStatus() != 4) {
            String errStr = ValidatorUtils.verifyStr(req, req.getClass());
            if (errStr != null) {
                throw new ZekeException(400, errStr);
            }
        }
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.ADMIN).get();
        AppPromotionNews one = getById(req.getId());
        if (one.getStatus() != 3 && one.getStatus() != 4) {
            throw new ZekeException(400, "该app推广消息记录不支持修改");
        }
        AppPromotionNews appPromotionNews = new AppPromotionNews();
        if (req.getStatus() == null || req.getStatus() != 4) {
            if (req.getSendDate() == null) {
                req.setSendDate(LocalDateTime.now());
                req.setStatus(2);
            } else {
                req.setStatus(1);
                appPromotionNews.setRemark("（于" + DateUtils.DTF_MD_HM.format(req.getSendDate()) + "时推送）");
            }
        }
        BeanUtils.copyProperties(req, appPromotionNews);
        appPromotionNews.setOperId(loginInfo.getId());
        appPromotionNews.setOperType(loginInfo.getType());
        appPromotionNews.setOperName(loginInfo.getName());
        UpdateWrapper<AppPromotionNews> uw = new UpdateWrapper<>();
        uw.lambda().eq(AppPromotionNews::getId, req.getId());
        if (req.getSendDate() == null) {
            uw.lambda().set(AppPromotionNews::getSendDate, null);
        }
        if (one.getStatus() != 4 && req.getStatus() == 4) {
            if (one.getStatus() == 3) {
                appPromotionNews.setRemark("");
            } else {
                appPromotionNews.setRemark("（于" + DateUtils.DTF_MD_HM.format(LocalDateTime.now()) + "时取消推送）");
            }
        }
        boolean flag = update(appPromotionNews, uw);
        if (!flag) {
            throw new ZekeException(400, "app推送消息记录修改失败");
        }
        if (req.getStatus() == 4) {
            return;
        }
        //直接发送延时3秒发
        long millis = 3000;
        if (req.getStatus() == NewsConstants.PromotionNewsStatus.WEAT_SEND) {
            //延时毫秒值
            millis = Duration.between(LocalDateTime.now(), appPromotionNews.getSendDate()).toMillis();
        }
        //定时推送消息
        DelayMqVo<String> delayMqVo = new DelayMqVo<>();
        delayMqVo.setT(String.valueOf(appPromotionNews.getId()));
        delayMqVo.setType(DelayMqVo.DelayMqVoType.APP_UNIPUSH_MSG);
        rabbitSend.sendMessageDelay(MQConfig.DELAY_EXCHANGE, MQConfig.DELAY_KEY, delayMqVo, millis);
    }

    /**
     * 查询app消息推广记录
     *
     * @param req
     * @return
     */
    @Override
    public IPage<AppPromotionNewsPageRes> queryAppPromotionNewsPage(AppPromotionNewsPageReq req) {
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus("1,2,3,4");
        }
        IPage<AppPromotionNewsPageRes> iPage = req.pagePojo();
        iPage = getBaseMapper().queryAppPromotionNewsPage(iPage, req);
        return iPage;
    }

    /**
     * 修改app消息推广记录状态
     *
     * @param id
     * @param status
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAppPromotionNewsStatus(Integer id, Integer status) {
        if (status != -1 && status != 4) {
            throw new ZekeException(400, "只支持删除消息和撤销操作");
        }
        AppPromotionNews appPromotionNews = getById(id);
        if (status == -1 && appPromotionNews.getStatus() != 4) {
            throw new ZekeException(400, "消息不支持删除");
        }
        if (status == 4 && appPromotionNews.getStatus() != 1) {
            throw new ZekeException(400, "消息不支持撤销");
        }
        AppPromotionNews update = new AppPromotionNews();
        update.setId(id);
        update.setStatus(status);
        if (status == 4) {
            update.setRemark("（于" + DateUtils.DTF_MD_HM.format(LocalDateTime.now()) + "时取消推送）");
        }
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(400, "修改状态失败");
        }
    }

    /**
     * 查询app消息推广记录详情
     *
     * @param id
     * @return
     */
    @Override
    public AppPromotionNewsDetailRes queryAppPromotionNewsDetail(Integer id) {
        AppPromotionNews appPromotionNews = getById(id);
        AppPromotionNewsDetailRes res = new AppPromotionNewsDetailRes();
        if (appPromotionNews != null) {
            BeanUtils.copyProperties(appPromotionNews, res);
        }
        return res;
    }

    /**
     * 重新推送/立即推送消息 通过cid推送unipush消息
     *
     * @param id   app消息id
     * @param type 1客户端调用 2程序内部调用
     */
    @Override
    public void reSendAppPromotionNews(Integer id, Integer type) {
        AppPromotionNews appPromotionNews = getById(id);

        if (appPromotionNews == null) {
            log.info("app推送消息,消息不存在:{}", id);
            return;
        }

        if (type == 2) {
            Duration duration = Duration.between(appPromotionNews.getSendDate(), LocalDateTime.now());
            if (appPromotionNews.getSendDate().isAfter(LocalDateTime.now()) && duration.toMillis() > 1) {
                //发送时机
                return;
            }
        }
        boolean b = false;
        Map<String,String> map = new HashMap();
        map.put("title", appPromotionNews.getTitle());
        map.put("body", appPromotionNews.getCopywriting());
        map.put("payload", JsonUtils.objectToStr(appPromotionNews));
        if (NewsConstants.PromotionNewsType.ALL == appPromotionNews.getSendType()) {
            //全员发送
            b = JiGuangPushUtil.jpushAll(map, null);
            List<String> cidByDocType = trustDeviceService.getCidByDocType(null);
            appPromotionNews.setSendNum(cidByDocType.size());

        }
        if (NewsConstants.PromotionNewsType.AUTH == appPromotionNews.getSendType()) {
            //已认证医生要发送
            List<String> cidByDocType = trustDeviceService.getCidByDocType(UserConstants.IsCertification.certification);
            if (cidByDocType == null || cidByDocType.isEmpty()) {
                log.info("app推送消息,没有认证医生:{}", id);
                return;
            }
            b = JiGuangPushUtil.jpushAll(map, cidByDocType);
            appPromotionNews.setSendNum(cidByDocType.size());
        }

        if (NewsConstants.PromotionNewsType.NO_AUTH == appPromotionNews.getSendType()) {
            //已认证医生要发送
            List<String> cidByDocType = trustDeviceService.getCidByDocType(UserConstants.IsCertification.not_certification);
            if (cidByDocType == null || cidByDocType.isEmpty()) {
                log.info("app推送消息,没有未认证医生:{}", id);
                return;
            }
            b = JiGuangPushUtil.jpushAll(map, cidByDocType);
            appPromotionNews.setSendNum(cidByDocType.size());
        }

        /* 这里是 个推推送app消息
        AppNewsSendReq appNewsSendReq = new AppNewsSendReq();
        BeanUtils.copyProperties(appPromotionNews, appNewsSendReq);
        //整个对象作参数传递, 不要content内容,太大
        appNewsSendReq.setContent(appPromotionNews.getCopywriting());
        appPromotionNews.setContent(null);
        appNewsSendReq.setParams(JsonUtils.objectToStr(appPromotionNews));

        if (NewsConstants.PromotionNewsType.ALL == appPromotionNews.getSendType()) {
            //全员发送
            b = geTuiUtils.pushAll(appNewsSendReq);
        }
        if (NewsConstants.PromotionNewsType.AUTH == appPromotionNews.getSendType()) {
            //已认证医生要发送
            List<String> cidByDocType = trustDeviceService.getCidByDocType(UserConstants.IsCertification.certification);
            if (cidByDocType == null || cidByDocType.isEmpty()) {
                log.info("app推送消息,没有认证医生:{}", id);
                return;
            }
            b = geTuiUtils.pushListByCid(cidByDocType, appNewsSendReq);
            appPromotionNews.setSendNum(cidByDocType.size());
        }

        if (NewsConstants.PromotionNewsType.NO_AUTH == appPromotionNews.getSendType()) {
            //已认证医生要发送
            List<String> cidByDocType = trustDeviceService.getCidByDocType(UserConstants.IsCertification.not_certification);
            if (cidByDocType == null || cidByDocType.isEmpty()) {
                log.info("app推送消息,没有未认证医生:{}", id);
                return;
            }
            b = geTuiUtils.pushListByCid(cidByDocType, appNewsSendReq);
            appPromotionNews.setSendNum(cidByDocType.size());
        }
        */

        if (b) {
            appPromotionNews.setStatus(NewsConstants.PromotionNewsStatus.SEND);
        } else {
            appPromotionNews.setSendNum(0);
            appPromotionNews.setStatus(NewsConstants.PromotionNewsStatus.SEND_ERROR);
            appPromotionNews.setRemark("（于" + DateUtils.DTF_MD_HM.format(LocalDateTime.now()) + "时推送失败，原因：异常导致无法发送）");
        }
        UpdateWrapper<AppPromotionNews> uw = new UpdateWrapper<>();
        uw.lambda().eq(AppPromotionNews::getId, appPromotionNews.getId())
                .set(AppPromotionNews::getStatus, appPromotionNews.getStatus())
                .set(AppPromotionNews::getRemark, appPromotionNews.getRemark())
                .set(AppPromotionNews::getSendNum, appPromotionNews.getSendNum());
        if (type == 1) {
            uw.lambda().set(AppPromotionNews::getSendDate, LocalDateTime.now()).set(AppPromotionNews::getRemark, "（于" + DateUtils.DTF_MD_HM.format(LocalDateTime.now()) + "时推送）");
        }
        boolean update = update(uw);
        if (!update) {
            logErrorService.presBackLog("修改消息状态失败", "app消息异常", "id=" + appPromotionNews.getId());
        }
    }

    /**
     * @description 消息推送成功收到
     * @param id:
     * @return void
     * @author czg
     * @date 2024/1/19 11:06
    */
    @Override
    public void appPromotionNewsReceiveNum(Integer id, String cid) {
        if (redisTemplate.opsForSet().isMember(RedisKeyConstants.APP_PROMOTION_RECEIVE_NUM, cid)){
            return;
        }
        UpdateWrapper wrapper = new UpdateWrapper();
        wrapper.eq("id", id);
        wrapper.setSql("receive_num = receive_num + 1");
        update(wrapper);
        redisTemplate.opsForSet().add(RedisKeyConstants.APP_PROMOTION_RECEIVE_NUM, cid);
        redisTemplate.expire(RedisKeyConstants.APP_PROMOTION_RECEIVE_NUM, 3, TimeUnit.HOURS);
    }

    /**
     * @description 消息推送成功被点击
     * @param id:
     * @return void
     * @author czg
     * @date 2024/1/19 11:19
    */
    @Override
    public void appPromotionNewsClickNum(Integer id, String cid) {
        if (redisTemplate.opsForSet().isMember(RedisKeyConstants.APP_PROMOTION_CLICK_NUM, cid)){
            return;
        }

        UpdateWrapper wrapper = new UpdateWrapper();
        wrapper.eq("id", id);
        wrapper.setSql("click_num = click_num + 1");
        update(wrapper);
        redisTemplate.opsForSet().add(RedisKeyConstants.APP_PROMOTION_CLICK_NUM, cid);
        redisTemplate.expire(RedisKeyConstants.APP_PROMOTION_CLICK_NUM, 3, TimeUnit.HOURS);
    }
}
